package com.ioog.domain.model;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Index;

@Entity
@Table(name="location")
public class Location implements Serializable {
	
	private static final long serialVersionUID = 1585722544068683686L;

	@Id@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="location_id")
	private Long id;
	
	@Column(name="location_name", length=50, nullable=false, unique=true)
	@Index(name="idx_location_name")
	private String name;
	
	@Column(name="latitude", nullable=false)
	private Float lat;
	
	@Column(name="longitude", nullable=false)
	private Float lng;
	
	@Embedded
	private Address address;
	
	@Enumerated
	@Column(name="location_state", nullable=false)
	private LocationState state;
	
	@OneToOne(cascade={CascadeType.ALL}, fetch=FetchType.LAZY)
	@JoinColumn(name="location_detail_id")
	private LocationDetail detail;
	
	@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE},fetch=FetchType.EAGER)
	@JoinTable(name="location_tags",
			joinColumns={@JoinColumn(name="location_id")},
			inverseJoinColumns={@JoinColumn(name="tag_id")})
	private Set<Tag> tags = new HashSet<Tag>();
	
	@ManyToOne(optional=false,cascade={CascadeType.PERSIST, CascadeType.MERGE},fetch=FetchType.EAGER)
	@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	@JoinTable(name="location_category",
			joinColumns={@JoinColumn(name="location_id")},
			inverseJoinColumns={@JoinColumn(name="category_id")})
	private Category category;
	
	@Temporal(TemporalType.TIMESTAMP)
    @Column(name="created", nullable = false, updatable = false)
    private Date created = new Date();
	
	public Location () { }

	public Location(String name, Float lat, Float lng, Address address) {
		super();
		this.name = name;
		this.lat = lat;
		this.lng = lng;
		this.address = address;
		this.state = LocationState.PENDING;
	}

	public Location(String name, Float lat, Float lng, Address address,
			LocationState state, Set<Tag> tags, Category category) {
		super();
		this.name = name;
		this.lat = lat;
		this.lng = lng;
		this.address = address;
		this.state = state;
		this.tags = tags;
		this.category = category;
	}

	public Location(String name, Float lat, Float lng, Address address,
			LocationDetail detail, Set<Tag> tags, Category category) {
		super();
		this.name = name;
		this.lat = lat;
		this.lng = lng;
		this.address = address;
		this.detail = detail;
		this.tags = tags;
		this.category = category;
	}

	public Long getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Float getLat() {
		return lat;
	}

	public void setLat(Float lat) {
		this.lat = lat;
	}

	public Float getLng() {
		return lng;
	}

	public void setLng(Float lng) {
		this.lng = lng;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public LocationState getState() {
		return state;
	}

	public LocationDetail getDetail() {
		return detail;
	}

	public void setDetail(LocationDetail detail) {
		this.detail = detail;
	}

	public Set<Tag> getTags() {
		return tags;
	}

	public void setTags(Set<Tag> tags) {
		this.tags = tags;
	}

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}
	
	public Date getCreated() {
		return created;
	}
	
	/* Common methods */
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((lat == null) ? 0 : lat.hashCode());
		result = prime * result + ((lng == null) ? 0 : lng.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Location other = (Location) obj;
		if (lat == null) {
			if (other.lat != null)
				return false;
		} else if (!lat.equals(other.lat))
			return false;
		if (lng == null) {
			if (other.lng != null)
				return false;
		} else if (!lng.equals(other.lng))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}	
	
	/* Business Methods */
	
	public void activate(){
		this.state = LocationState.ACTIVE;
	}
	
	public void pending(){
		this.state = LocationState.PENDING;
	}
}
